package cn.bonoon.kernel.web.controllers;

import java.lang.reflect.AnnotatedElement;

import org.springframework.web.bind.annotation.RequestMapping;

import cn.bonoon.handler.impl.StandardAutoManager;
import cn.bonoon.kernel.events.PanelEvent;
import cn.bonoon.kernel.support.entities.Persistable;
import cn.bonoon.kernel.support.services.GenericService;
import cn.bonoon.kernel.support.services.IService;
import cn.bonoon.kernel.support.services.SearchService;
import cn.bonoon.kernel.util.ClassHelper;
import cn.bonoon.kernel.util.MD5Util;
import cn.bonoon.kernel.util.StringHelper;
import cn.bonoon.kernel.web.ButtonEventType;
import cn.bonoon.kernel.web.ButtonRefreshType;
import cn.bonoon.kernel.web.annotations.grid.GridStandardDefinition;
import cn.bonoon.kernel.web.handlers.AbstractHandlerRegister;
import cn.bonoon.kernel.web.handlers.DialogDetailHandler;
import cn.bonoon.kernel.web.handlers.DialogInsertHandler;
import cn.bonoon.kernel.web.handlers.DialogUpdateHandler;
import cn.bonoon.kernel.web.handlers.OperateIdHandler;
import cn.bonoon.kernel.web.handlers.OperateIdsHandler;
import cn.bonoon.kernel.web.html.grid.StandardGridBuilder;
import cn.bonoon.kernel.web.models.PanelModel;
import cn.bonoon.kernel.web.resources.StandardResourceRepository;

/**
 * <pre>
 * 子类主要是重写{@link #initLayoutGrid(LayoutGridRegister)}方法
 * 
 * {@link #itemClass()}方法会自动解析，一般情况下可以不需要重写
 * </pre>
 * 
 * <pre>
 * 特殊要求：每一个继承<code>AbstractLayoutGridController</code>的控制器只允许在<code>@RequestMapping</code>定义一个路径；
 * 如果定义了多个路径则无法被解析；目前只能允许定义一个路径，以后有更好的算法，则会可以定义更多的路径
 * 
 * 虽然只能定义一个访问的路径，但该访问的路径可以被定义成多个菜单，不同菜单的访问的key不一样
 * </pre>
 * <pre>
 * V3
 * 加载数据和提交操作的两个处理事件分开
 * 路径需要分三种：
 * 1.弹出对话框
 * 2.加载数据
 * 3.提交数据
 * 这些路径上应该下放的信息包括：
 * </pre>
 * @author jackson
 * @see AbstractLayoutController
 */
public abstract class AbstractGridController<E extends Persistable, R> extends AbstractLayoutController{

	/**
	 * 自动生成的模板文件内容，用于velocity模板引擎的加载
	 */
	protected StandardResourceRepository resource;
	
	/**
	 * <pre>
	 * 使用该父类的子类，通过自动生成的界面（如：datagrid,dialog等）或者自定义的界面都应该使用这个id
	 * 
	 * 这个id在控制器初始化后创建，并且不能修改
	 * </pre>
	 * 即：grid id
	 */
	protected String id;

	/**
	 * 用于处理数据的查询和数据加载的接口
	 */
	protected final SearchService<E> searchService;

	/**
	 * <pre>
	 * 指定列表的数据结构对象，这是必须指定的
	 * 
	 * 子类可以不用重写这个方法
	 * </pre>
	 * @return
	 */
	protected Class<R> itemClass(){
		return ClassHelper.getActualTypeArgument(getClass(), AbstractGridController.class, 1);
	}
	
	/**
	 * 构造函数
	 */
	protected AbstractGridController(SearchService<E> searchService){
		this.searchService = searchService;
	}
	
	/**
	 * <pre>
	 * 在子类重写本方法的时候，可以使用{@link GridStandardDefinition}来指定生成的界面的一些按钮、属性定义等。
	 * <font color="red">注意：如果使用按钮，则必须该访问返回一个{@link GenericService}对象，否则无法自动添加按钮</font>
	 * </pre>
	 */
	protected IService<E> initLayoutGrid(LayoutGridRegister register) throws Exception { return null; }
	
	protected class LayoutGridRegister extends AbstractHandlerRegister{
		
		public LayoutGridRegister() throws Exception {
			super(id, itemClass(), autoManager);
		}

//		public void add(String name, Object value){
//			gridBuilder.add(name, value);
//		}
		
		public void init(AnnotatedElement aEle) throws Exception{
			this.gridBuilder.initLoader(searchService, aEle);
		}
		
		/**
		 * <pre>
		 * 如果按钮的访问路径是自定义的，则可以直接赋值
		 * </pre>
		 * @param name
		 * @param url
		 * @param eventType
		 * @return
		 */
		public GridToolbarResolver toolbar(String name, String url, ButtonEventType eventType, ButtonRefreshType refresh) {
			GridToolbarResolver gtr = gridBuilder.addToolbar();
			setButton(gtr, name, url, eventType);
			gtr.setRefreshType(refresh);
			return gtr;
		}
		public GridToolbarResolver toolbar(String name, String url, ButtonEventType eventType) {
			GridToolbarResolver gtr = gridBuilder.addToolbar();
			setButton(gtr, name, url, eventType);
			return gtr;
		}
		public GridButtonResolver button(String name, String url, ButtonEventType eventType) {
			GridButtonResolver gbr = gridBuilder.addButton();
			setButton(gbr, name, url, eventType);
			return gbr;
		}
		
		public GridButtonResolver button(String name, String url, ButtonEventType eventType, ButtonRefreshType refresh) {
			GridButtonResolver gbr = gridBuilder.addButton();
			setButton(gbr, name, url, eventType);
			gbr.setRefreshType(refresh);
			return gbr;
		}
		
		private void setButton(AbstractGridResolver btn, String name, String url, ButtonEventType eventType){
			btn.setName(name);
			if(url.startsWith("/")){
				btn.setUrl(toUrl(url));
			}else{
				btn.setUrl(url);
			}
			btn.setEventType(eventType);
			btn.passByParameter();
		}
	}

	/**
	 * <pre>
	 * 界面资源的解释，子类可以根据已解释的资源再进行资源的合并
	 * 
	 * 这里解释的是grid列表(datagrid或treegrid)，子类可以追加查询条件、右边栏和左边栏等
	 * </pre>
	 * 
	 * @param grid
	 * @return
	 */
	protected StandardResourceRepository initResource(StandardResourceRepository grid, StandardAutoManager initializer){ return grid; }
	
	/**
	 * 解释所定义的按钮，并为所有按钮生成js事件及后台处理事件
	 */
	@Override
	public void afterPropertiesSet() throws Exception {
		super.afterPropertiesSet();
		//解析当前路径
		RequestMapping rm = this.getClass().getAnnotation(RequestMapping.class);
		id = MD5Util.getMD5(StringHelper.join(",", rm.value()));

		/* *****************************************************************
		 * 
		 * 以下属性只需要来做控制器创建时的初始化工作，初始化处理结束，
		 * 这些属性是可以被释放掉的，以便更好的利用内存空间
		 * 
		 * *****************************************************************/
		LayoutGridRegister register = new LayoutGridRegister();//局部变量，初始化结束后应该可回收
		
		//可以使用注解的方式进行按钮的定义
		Class<?> targetClass = getClass();
		AnnotatedElement milg = null;
		for(;;){
			try{
				milg = targetClass.getDeclaredMethod("initLayoutGrid", LayoutGridRegister.class);
				if(null != milg){
					break;
				}
			}catch(Exception ex){
				targetClass = targetClass.getSuperclass();
				if(AbstractGridController.class.equals(targetClass)){
					break;
				}
			}
		}
		if(null == milg){
			milg = getClass();
		}
//		Method milg = targetClass.getDeclaredMethod("initLayoutGrid", LayoutGridRegister.class);
		GridStandardDefinition go = milg.getAnnotation(GridStandardDefinition.class);
		if(null != go){
			IService<E> service = initLayoutGrid(register);
			if(null != service){
				if(!Object.class.equals(go.detailClass())){
					DialogDetailHandler.button(register, service, go.detailClass()).ordinal(10);
				}
				if(service instanceof GenericService){
					GenericService<E> gs = (GenericService<E>)service;
					if(!Object.class.equals(go.insertClass())){
						GridToolbarResolver gtr = DialogInsertHandler.toolbar(register, gs, go.insertClass());
						gtr.ordinal(10);
						if(go.defaultClosable()){
							gtr.setCloseButtonKey("closeinsertbutton");
						}
					}
					if(!Object.class.equals(go.updateClass())){
						GridButtonResolver gbr = DialogUpdateHandler.button(register, gs, go.updateClass());
						gbr.status(go.editStatus()).ordinal(20);
						if(go.defaultClosable()){
							gbr.setCloseButtonKey("closeupdatebutton");
						}
					}
					if(go.autoOperation()){
						if(gs.isStatusable()){
							if(go.toolbarable()){
								OperateIdsHandler.resume(register, gs).status(-1, 0).ordinal(50);
								OperateIdsHandler.forbid(register, gs).status(0, 1).ordinal(60);
							}
							OperateIdHandler.resume(register, gs).status(-1, 0).ordinal(50);
							OperateIdHandler.forbid(register, gs).status(0, 1).ordinal(60);
						}
						if(gs.isMovable()){
							//TODO 可移动的情况的处理
						}
					}
					if(go.deleteOperation()){
						if(go.toolbarable()){
							GridToolbarResolver gtr = OperateIdsHandler.delete(register, gs);
							gtr.status(go.editStatus()).ordinal(100);
							if(go.defaultClosable()){
								gtr.setCloseButtonKey("closeinsertbutton");
							}
						}
						GridButtonResolver gbr = OperateIdHandler.delete(register, gs);
						gbr.status(go.editStatus()).ordinal(100);
						if(go.defaultClosable()){
							gbr.setCloseButtonKey("closeupdatebutton");
						}
					}
				}
			}
		}
		
		register.init(milg);
		
		StandardGridBuilder gridBuilder = register.getGridBuilder();
		gridBuilder.parse();
		StandardResourceRepository gridResource = new StandardResourceRepository(gridBuilder.getName(), gridBuilder);
		this.resource = initResource(gridResource, register.getManager());
	}
	
	@Override
	protected void onLoad(PanelEvent event) {
		PanelModel model = event.getModel();
		event.setVmPath(resource.render(model, "gridId"));
	}
}
